<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>存储线时段分布</title>
  <!-- <script src="https://cdn.tailwindcss.com"></script> -->
  <script src="js/tailwindcss.js"></script>
  <link href="css/all.min.css" rel="stylesheet">
  
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#3b82f6',
            secondary: '#10b981',
            accent: '#6366f1',
            neutral: '#f3f4f6',
            dark: '#1f2937',
            danger: '#ef4444',
            warning: '#f59e0b',
            info: '#06b6d4'
          },
          fontFamily: {
            inter: ['Inter', 'system-ui', 'sans-serif'],
          },
        }
      }
    }
  </script>
  
  <style type="text/tailwindcss">
    @layer utilities {
      .content-auto {
        content-visibility: auto;
      }
      .scrollbar-hide::-webkit-scrollbar {
        display: none;
      }
      .scrollbar-hide {
        -ms-overflow-style: none;
        scrollbar-width: none;
      }
      .timeline-grid {
        position: relative;
      }
      .task-bar {
        @apply rounded-md shadow-md flex items-center overflow-hidden transition-all hover:shadow-lg;
      }
      .status-dot {
        @apply w-2 h-2 rounded-full inline-block mr-1.5;
      }
      .time-marker-major {
        @apply relative bg-gray-200;
      }
      .time-marker-date {
        @apply relative bg-gray-300 font-bold;
      }
      .fixed-width {
        width: 6rem;
        flex-shrink: 0;
      }
      .border-right-major {
        @apply after:absolute after:top-0 after:bottom-0 after:right-0 after:w-px after:bg-gray-300;
      }
      .border-right-date {
        @apply after:absolute after:top-0 after:bottom-0 after:right-0 after:w-px after:bg-gray-400;
      }
      .sticky-header {
        @apply sticky top-0 z-10 bg-white/90 backdrop-blur-sm;
      }
      .timeline-container {
        @apply flex h-full overflow-auto;
      }
      .task-list-header {
        @apply border-r border-b border-gray-200 p-1 bg-gray-200 font-semibold;
      }
      .timeline-header-cell {
        @apply border-b border-gray-200 bg-gray-200 relative;
      }
      .task-list-item {
        @apply border-r border-b border-gray-400 p-3 flex flex-col justify-center;
      }
    }
  </style>
</head>
<body class="font-inter bg-gray-50 text-dark">
  <div class="min-h-screen flex flex-col">
    <!-- 顶部导航栏 -->
    <header class="bg-white shadow-sm sticky top-0 z-50">
      <div class="container mx-auto px-4 py-4 flex justify-between items-center">
        <div class="flex items-center space-x-2">
          <i class="fa fa-bar-chart text-primary text-2xl"></i>
          <h1 class="text-xl font-bold text-dark">存储线时段分布</h1>
        </div>
        <div class="flex items-center space-x-4">
          <div class="flex items-center space-x-2 bg-gray-100 rounded-lg px-3 py-1.5">
            <i class="fa fa-search text-gray-400"></i>
            <input type="text" placeholder="搜索线号..." class="bg-transparent outline-none text-sm w-48">
          </div>
          <div class="flex items-center space-x-2">
            <button class="bg-primary hover:bg-primary/90 text-white rounded-lg px-4 py-1.5 transition-all flex items-center space-x-1.5">
              <i class="fa fa-refresh"></i>
              <span>刷新</span>
            </button>
            <button class="bg-white border border-gray-300 hover:bg-gray-50 text-dark rounded-lg px-4 py-1.5 transition-all flex items-center space-x-1.5">
              <i class="fa fa-download"></i>
              <span>导出</span>
            </button>
          </div>
        </div>
      </div>
    </header>

    <!-- 主要内容区域 -->
    <main class="flex-grow container mx-auto px-4 py-6">
      <!-- 甘特图控制面板 -->
      <div class="bg-white shadow-sm p-2 mb-1 flex flex-wrap items-center justify-between gap-4">
        <div class="flex items-center space-x-4">
          <div class="flex items-center space-x-2">
            <div class="flex items-center bg-gray-100 rounded-lg p-1">
              <button id="zoomOut" title="缩小时间轴" class="w-8 h-8 flex items-center justify-center rounded hover:bg-gray-200 transition-colors">
                <i class="fa fa-search-minus text-gray-600"></i>
              </button>
              <div class="w-px h-6 bg-gray-300 mx-1"></div>
              <button id="zoomIn" title="放大时间轴" class="w-8 h-8 flex items-center justify-center rounded hover:bg-gray-200 transition-colors">
                <i class="fa fa-search-plus text-gray-600"></i>
              </button>
            </div>
          </div>
        </div>
        <div class="flex items-center space-x-4">
          <div class="flex items-center space-x-4">
            <div class="flex items-center space-x-2">
              <span class="status-dot bg-danger"></span>
              <span class="text-sm">超时未释放</span>
            </div>
            <div class="flex items-center space-x-2">
              <span class="status-dot bg-warning"></span>
              <span class="text-sm">即将超时</span>
            </div>
            <div class="flex items-center space-x-2">
              <span class="status-dot bg-secondary"></span>
              <span class="text-sm">已到时间</span>
            </div>
            <div class="flex items-center space-x-2">
              <span class="status-dot bg-primary"></span>
              <span class="text-sm">未到时间</span>
            </div>
          </div>
        </div>
      </div>

      <!-- 甘特图区域 -->
      <div class="bg-white shadow-sm overflow-hidden">
        <div class="timeline-container h-[600px]">
          <!-- 左侧任务列表 -->
          <div class="fixed-width flex flex-col">
            <!-- 任务列表标题 -->
            <div class="task-list-header h-[30px]">
              线号/状态
            </div>
            
            <!-- 任务列表内容 -->
            <div id="taskList" class="flex-grow overflow-y-auto scrollbar-hide">
              <!-- 任务名称将由JavaScript动态生成 -->
            </div>
          </div>
          
          <!-- 时间轴内容 -->
          <div class="flex-grow relative">
            <!-- 时间轴头部 -->
            <div id="timelineHeader" class="flex sticky top-0 z-10 bg-white/90 backdrop-blur-sm">
              <!-- 时间刻度将由JavaScript动态生成 -->
            </div>
            
            <!-- 时间轴网格和内容 -->
            <div class="relative">
              <div id="timelineGrid" class="absolute top-0 left-0 min-h-full min-w-full timeline-grid">
                <!-- 网格线将由JavaScript动态生成 -->
              </div>
              <div id="timelineContent" class="absolute top-0 left-0 min-h-full min-w-full">
                <!-- 甘特图任务条将由JavaScript动态生成 -->
              </div>
              <div id="taskRowBorders" class="absolute top-0 left-0 min-h-full min-w-full pointer-events-none">
                <!-- 任务行边框将由JavaScript动态生成 -->
              </div>
            </div>
          </div>
        </div>
      </div>
    </main>

    <!-- 页脚 -->
    <footer class="bg-white border-t border-gray-200 py-2">
      <div class="container mx-auto px-2 text-center text-gray-500 text-sm">
        <p>© 2025 WMS</p>
      </div>
    </footer>
  </div>

  <script>
    // 原始数据 - 包含跨天的任务
    const data = [
      {
        lineNo: 'D101',
        status: '释放中',
        tasks: [
          { begin: '2025-07-30 08:30:00', end: '2025-07-30 10:30:00', quantity: 15 },
          { begin: '2025-07-30 10:00:00', end: '2025-07-30 13:00:00', quantity: 3 },
          { begin: '2025-07-30 12:00:00', end: '2025-07-30 15:00:00', quantity: 5 },
          { begin: '2025-07-30 12:30:00', end: '2025-07-30 15:30:00', quantity: 3 }
        ]
      },
      {
        lineNo: 'D102',
        status: '正常',
        tasks: [
          { begin: '2025-07-30 23:30:00', end: '2025-07-31 01:30:00', quantity: 5 },
          { begin: '2025-07-30 11:00:00', end: '2025-07-30 13:00:00', quantity: 15 },
          { begin: '2025-07-30 14:30:00', end: '2025-07-30 16:30:00', quantity: 3 }
        ]
      },
      {
        lineNo: 'D103',
        status: '正常',
        tasks: [
          { begin: '2025-07-30 21:00:00', end: '2025-07-31 01:00:00', quantity: 5 },
          { begin: '2025-07-30 13:30:00', end: '2025-07-30 15:30:00', quantity: 15 },
          { begin: '2025-07-30 15:00:00', end: '2025-07-30 17:00:00', quantity: 3 }
        ]
      },
      {
        lineNo: 'D104',
        status: '正常',
        tasks: [
          { begin: '2025-07-30 18:00:00', end: '2025-07-31 02:00:00', quantity: 5 },
          { begin: '2025-07-30 14:30:00', end: '2025-07-30 16:30:00', quantity: 15 },
          { begin: '2025-07-30 16:00:00', end: '2025-07-30 18:00:00', quantity: 3 }
        ]
      },
      {
        lineNo: 'D105',
        status: '待释放',
        tasks: [
          { begin: '2025-07-30 23:30:00', end: '2025-07-31 01:30:00', quantity: 5 },
          { begin: '2025-07-30 15:00:00', end: '2025-07-30 17:00:00', quantity: 15 },
          { begin: '2025-07-30 17:30:00', end: '2025-07-30 19:30:00', quantity: 3 }
        ]
      },
      {
        lineNo: 'D203',
        status: '正常',
        tasks: [
          { begin: '2025-07-30 21:00:00', end: '2025-07-31 01:00:00', quantity: 5 },
          { begin: '2025-07-30 13:30:00', end: '2025-07-30 15:30:00', quantity: 15 },
          { begin: '2025-07-30 15:00:00', end: '2025-07-30 17:00:00', quantity: 3 }
        ]
      },
      {
        lineNo: 'D204',
        status: '正常',
        tasks: [
          { begin: '2025-07-30 18:00:00', end: '2025-07-31 02:00:00', quantity: 5 },
          { begin: '2025-07-30 14:30:00', end: '2025-07-30 16:30:00', quantity: 15 },
          { begin: '2025-07-30 16:00:00', end: '2025-07-30 18:00:00', quantity: 3 }
        ]
      },
      {
        lineNo: 'D205',
        status: '待释放',
        tasks: [
          { begin: '2025-07-30 23:30:00', end: '2025-07-31 01:30:00', quantity: 5 },
          { begin: '2025-07-30 15:00:00', end: '2025-07-30 17:00:00', quantity: 15 },
          { begin: '2025-07-30 17:30:00', end: '2025-07-30 19:30:00', quantity: 3 }
        ]
      }
    ];

    // 配置参数
    const config = {
      zoomLevel: 1,
      minZoom: 0.5,
      maxZoom: 2,
      zoomStep: 0.1,
      taskRowHeight: 80,    // 每个任务行的高度
      timeScale: 100,       // 每小时对应的像素数
      startTime: new Date('2025-07-29 18:00:00'),  // 调整开始时间以包含跨天数据
      endTime: new Date('2025-08-01 01:00:00'),    // 调整结束时间以包含跨天数据
      maxOverlap: 5,        // 最大重叠层数
    };

    // DOM元素
    const timelineHeader = document.getElementById('timelineHeader');
    const taskList = document.getElementById('taskList');
    const timelineContent = document.getElementById('timelineContent');
    const timelineGrid = document.getElementById('timelineGrid');
    const taskRowBorders = document.getElementById('taskRowBorders');
    const timelineContainer = document.querySelector('.timeline-container');

    // 同步滚动
    function syncScroll() {
      // 由于结构变化，可能需要调整滚动同步逻辑
    }

    // 获取时间点的像素位置（使用统一计算方法，避免误差累积）
    function getPixelPosition(date) {
      const timeOffset = (date - config.startTime) / (1000 * 60 * 60);
      return timeOffset * config.timeScale * config.zoomLevel;
    }

    // 渲染时间轴头部和网格线
    function renderTimeline() {
      timelineHeader.innerHTML = '';
      timelineGrid.innerHTML = '';
      
      const totalHours = (config.endTime - config.startTime) / (1000 * 60 * 60);
      const hourWidth = config.timeScale * config.zoomLevel;
      
      // 设置宽度
      timelineHeader.style.minWidth = `${totalHours * hourWidth}px`;
      timelineGrid.style.minWidth = `${totalHours * hourWidth}px`;
      
      // 生成时间刻度和网格线
      const currentTime = new Date(config.startTime);
      let previousDate = null;
      
      while (currentTime < config.endTime) {
        const hour = currentTime.getHours();
        const minute = currentTime.getMinutes();
        const isMidnight = (hour === 0 && minute === 0) || previousDate==null;
        const isMajor = minute === 0;
        const timeLabel = isMidnight 
          ? `${currentTime.getMonth()+1}-${currentTime.getDate()}` 
          : `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
        
        // 使用统一的计算方法获取像素位置
        const pixelOffset = getPixelPosition(currentTime);
        
        // 创建时间轴头部的时间标记
        const timeMarker = document.createElement('div');
        timeMarker.className = isMidnight 
          ? 'time-marker-date border-right-date timeline-header-cell' 
          : 'time-marker-major border-right-major timeline-header-cell';
        timeMarker.style.width = `${hourWidth}px`;
        timeMarker.style.height = `30px`;
        
        const label = document.createElement('div');
        label.className = isMidnight 
          ? 'absolute bottom-1 left-1 text-xs font-bold text-gray-800' 
          : 'absolute bottom-1 left-1 text-xs font-medium text-gray-700';
        label.textContent = timeLabel;
        
        timeMarker.appendChild(label);
        timelineHeader.appendChild(timeMarker);
        
        // 创建内容区域的网格线
        const gridLine = document.createElement('div');
        gridLine.className = isMidnight 
          ? 'absolute top-0 bottom-0 w-px bg-gray-400' 
          : 'absolute top-0 bottom-0 w-px bg-gray-200';
        gridLine.style.left = `${pixelOffset}px`;
        timelineGrid.appendChild(gridLine);
        
        // 记录当前日期，用于识别日期变化
        previousDate = currentTime.getDate();
        
        // 每次增加一小时
        currentTime.setHours(currentTime.getHours() + 1);
      }
    }

    // 计算每个任务行的最大重叠层数
    function calculateMaxOverlap() {
      const maxOverlaps = [];
      data.forEach((line) => {
        const sortedTasks = [...line.tasks].sort((a, b) => 
          new Date(a.begin) - new Date(b.begin)
        );
        const lanes = [];
        sortedTasks.forEach(task => {
          let laneIndex = 0;
          while (laneIndex < lanes.length) {
            let canPlace = true;
            for (let i = 0; i < lanes[laneIndex].length; i++) {
              if (isOverlapping(lanes[laneIndex][i], task)) {
                canPlace = false;
                break;
              }
            }
            if (canPlace) break;
            laneIndex++;
          }
          if (laneIndex >= lanes.length) {
            lanes.push([task]);
          } else {
            lanes[laneIndex].push(task);
          }
        });
        maxOverlaps.push(lanes.length);
      });
      return maxOverlaps;
    }

    // 渲染任务列表
    function renderTaskList() {
      taskList.innerHTML = '';
      const maxOverlaps = calculateMaxOverlap();
      const taskHeight = 15; // 每个任务条的高度
      data.forEach((item, index) => {
        const taskItem = document.createElement('div');
        const rowHeight = maxOverlaps[index] * taskHeight + 20; // 20 是额外的间距
        taskItem.className = `task-list-item h-[${rowHeight}px] ${index === data.length - 1 ? 'border-b-0' : ''}`;
        
        const lineNo = document.createElement('div');
        lineNo.className = 'font-medium text-sm';
        lineNo.textContent = item.lineNo;
        
        const status = document.createElement('div');
        status.className = `text-xs ${getStatusColorClass(item.status)}`;
        status.innerHTML = `<span class="status-dot ${getStatusColorClass(item.status)}"></span>${item.status}`;
        
        taskItem.appendChild(lineNo);
        taskItem.appendChild(status);
        taskList.appendChild(taskItem);
      });
      
      renderTaskRowBorders();
    }

    // 渲染任务行的边框（延伸到时间轴内容区域）
    function renderTaskRowBorders() {
      taskRowBorders.innerHTML = '';
      const maxOverlaps = calculateMaxOverlap();
      const taskHeight = 15; // 每个任务条的高度
      const totalHours = (config.endTime - config.startTime) / (1000 * 60 * 60);
      const totalWidth = totalHours * config.timeScale * config.zoomLevel;
      
      let cumulativeHeight = 0;
      for (let i = 1; i < data.length; i++) {
        const rowHeight = maxOverlaps[i - 1] * taskHeight + 20; // 20 是额外的间距
        cumulativeHeight += rowHeight;
        const border = document.createElement('div');
        border.className = 'absolute left-0 right-0 h-px bg-gray-400';
        border.style.top = `${cumulativeHeight}px`;
        border.style.width = `${totalWidth}px`;
        taskRowBorders.appendChild(border);
      }
    }

    // 检查两个任务是否重叠
    function isOverlapping(task1, task2) {
      const start1 = getPixelPosition(new Date(task1.begin));
      const end1 = getPixelPosition(new Date(task1.end));
      const start2 = getPixelPosition(new Date(task2.begin));
      const end2 = getPixelPosition(new Date(task2.end));
      
      return start1 < end2 && start2 < end1;
    }

    // 渲染甘特图任务条
    function renderTimelineTasks() {
      timelineContent.innerHTML = '';
      
      // 渲染每个任务的时间段
      data.forEach((line, lineIndex) => {
        // 计算任务的Y位置（从上到下排列）
        const maxOverlaps = calculateMaxOverlap();
        const taskHeight = 15; // 每个任务条的高度
        const rowHeight = maxOverlaps[lineIndex] * taskHeight + 20; // 20 是额外的间距
        let cumulativeHeight = 0;
        for (let i = 0; i < lineIndex; i++) {
          cumulativeHeight += maxOverlaps[i] * taskHeight + 20; // 20 是额外的间距
        }
        const baseY = cumulativeHeight;
        
        // 对任务进行排序
        const sortedTasks = [...line.tasks].sort((a, b) => 
          new Date(a.begin) - new Date(b.begin)
        );
        
        // 处理重叠的任务
        const lanes = []; // 存储每个轨道的任务
        const lanePositions = []; // 存储每个轨道的像素位置
        
        sortedTasks.forEach(task => {
          const startDate = new Date(task.begin);
          const endDate = new Date(task.end);
          
          // 使用统一的计算方法获取像素位置
          const startPixel = getPixelPosition(startDate);
          const endPixel = getPixelPosition(endDate);
          const duration = endPixel - startPixel;
          
          // 计算任务条的颜色和高度
          //const baseHeight = 20 + Math.min(15, task.quantity * 2); // 根据行李数量调整高度
          //const height = Math.max(15, baseHeight / config.maxOverlap); // 考虑重叠后的高度
          const height = 15;
          const color = getTaskColor(startDate,endDate);
          
          // 查找可以放置此任务的轨道
          let laneIndex = 0;
          while (laneIndex < lanes.length) {
            let canPlace = true;
            // 检查当前轨道上的所有任务是否与当前任务重叠
            for (let i = 0; i < lanes[laneIndex].length; i++) {
              if (isOverlapping(lanes[laneIndex][i], task)) {
                canPlace = false;
                break;
              }
            }
            if (canPlace) break;
            laneIndex++;
          }
          
          // 如果没有找到合适的轨道，创建一个新的
          if (laneIndex >= lanes.length) {
            lanes.push([task]);
            lanePositions.push(baseY + (rowHeight - maxOverlaps[lineIndex] * height) / 2 + laneIndex * height);
          } else {
            // 将任务添加到当前轨道
            lanes[laneIndex].push(task);
          }
          
          // 创建任务条
          const taskBar = document.createElement('div');
          taskBar.className = 'task-bar absolute z-10';
          taskBar.style.left = `${startPixel}px`;
          taskBar.style.top = `${lanePositions[laneIndex]}px`;
          taskBar.style.width = `${duration}px`;
          taskBar.style.height = `${height - 2}px`; // 减2px为了视觉间隔
          taskBar.style.backgroundColor = color;
          
          // 添加任务信息
          const taskInfo = document.createElement('div');
          taskInfo.className = 'w-full h-full flex items-center justify-between p-1';
          
          const startTime = document.createElement('div');
          startTime.className = 'text-white text-xs px-1';
          startTime.textContent = formatTime(task.begin);
          
          const quantity = document.createElement('div');
          quantity.className = 'text-white text-xs font-bold';
          quantity.textContent = task.quantity;
          
          const endTime = document.createElement('div');
          endTime.className = 'text-white text-xs px-1';
          endTime.textContent = formatTime(task.end);
          
          taskInfo.appendChild(startTime);
          //taskInfo.appendChild(quantity);
          taskInfo.appendChild(endTime);
          
          taskBar.appendChild(taskInfo);
          
          // 添加到时间轴
          timelineContent.appendChild(taskBar);
        });
      });
      
      // 设置内容区域高度
      const maxOverlaps = calculateMaxOverlap();
      const taskHeight = 15; // 每个任务条的高度
      let totalHeight = 0;
      maxOverlaps.forEach((overlap) => {
        totalHeight += overlap * taskHeight + 20; // 20 是额外的间距
      });
      timelineContent.style.height = `${totalHeight}px`;
      timelineGrid.style.height = `${totalHeight}px`;
      taskRowBorders.style.height = `${totalHeight}px`;
    }

    // 格式化时间 - 显示日期和时间
    function formatTime(dateTimeStr) {
      const date = new Date(dateTimeStr);
      //const datePart = `${date.getMonth()+1}/${date.getDate()}`;
      const timePart = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
      return `${timePart}`;
    }

    // 获取状态对应的颜色类
    function getStatusColorClass(status) {
      switch (status) {
        case '释放中':
          return 'text-danger';
        case '正常':
          return 'text-secondary';
        case '待释放':
          return 'text-warning';
        default:
          return 'text-gray-500';
      }
    }

    // 获取任务条颜色
    function getTaskColor(startDate,endDate) {
      // 根据时间调整颜色深度
      const baseColors = [
        'rgba(59, 130, 246, 0.7)',  // 蓝色
        'rgba(16, 185, 129, 0.7)',  // 绿色
        'rgba(99, 102, 241, 0.7)',  // 靛蓝色
        'rgba(249, 115, 22, 0.7)',  // 橙色
        'rgba(239, 68, 68, 0.7)'   // 红色
      ];
      
      const now = new Date();
      const diffMs = endDate - now; //ms
      const diffMinutes = Math.round(diffMs / (1000 * 60)); //分钟
      console.log(`距离最迟释放时间还有 ${diffMinutes} 分钟`);
      var r = baseColors[0];
      if(diffMinutes>=30) r = baseColors[0];
      if(startDate<now) r = baseColors[1];
      if(diffMinutes<30 && diffMinutes>=0) r = baseColors[3];
      if(diffMinutes<0) r = baseColors[4];
      return r;
    }

    // 缩放时间轴
    function zoomTimeline(zoomAmount) {
      const newZoom = config.zoomLevel + zoomAmount;
      
      if (newZoom >= config.minZoom && newZoom <= config.maxZoom) {
        config.zoomLevel = newZoom;
        renderTimeline();
        renderTaskList(); // 重新渲染任务列表以更新边框
        renderTimelineTasks();
      }
    }

    // 初始化
    function init() {
      // 自动计算时间范围
      calculateTimeRange();
      renderTimeline();
      renderTaskList();
      renderTimelineTasks();
      
      // 添加滚动事件监听
      timelineContainer.addEventListener('scroll', syncScroll);
    }

    // 自动计算时间范围，包含所有任务的开始和结束时间
    function calculateTimeRange() {
      let minDate = new Date('2100-01-01');
      let maxDate = new Date('2000-01-01');
      
      // 找出所有任务中的最早开始时间和最晚结束时间
      data.forEach(line => {
        line.tasks.forEach(task => {
          const start = new Date(task.begin);
          const end = new Date(task.end);
          
          if (start < minDate) minDate = start;
          if (end > maxDate) maxDate = end;
        });
      });
      // 转为整点
      minDate.setMinutes(0, 0, 0); 
      maxDate.setMinutes(0, 0, 0); 
      // 扩展时间范围，在前后各增加2小时
      config.startTime = new Date(minDate.getTime() - 2 * 60 * 60 * 1000);
      config.endTime = new Date(maxDate.getTime() + 2 * 60 * 60 * 1000);
    }

    // 事件监听
    document.getElementById('zoomIn').addEventListener('click', () => zoomTimeline(config.zoomStep));
    document.getElementById('zoomOut').addEventListener('click', () => zoomTimeline(-config.zoomStep));

    // 鼠标滚轮缩放
    timelineContainer.addEventListener('wheel', (e) => {
      e.preventDefault();
      const zoomAmount = e.deltaY < 0 ? config.zoomStep : -config.zoomStep;
      zoomTimeline(zoomAmount);
    });

    // 页面加载完成后初始化
    document.addEventListener('DOMContentLoaded', init);
  </script>
</body>
</html>